Specify the tissue of interest, run the boilerplate code which sets up the functions and environment, load the tissue object.
tissue_of_interest = "Mammary"
library(here)
source(here("00_data_ingest", "02_tissue_analysis_rmd", "boilerplate.R"))
tiss <- load_tissue_facs(tissue_of_interest)
Performing log-normalization
0% 10 20 30 40 50 60 70 80 90 100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
[1] "Scaling data matrix"
|
| | 0%
|
|==========================================================================================================================| 100%
Calculating gene means
0% 10 20 30 40 50 60 70 80 90 100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating gene variance to mean ratios
0% 10 20 30 40 50 60 70 80 90 100%
|----|----|----|----|----|----|----|----|----|----|
**************************************************|


Later on (in FindClusters and TSNE) you will pick a number of principal components to use. This has the effect of keeping the major directions of variation in the data and, ideally, supressing noise. There is no correct answer to the number to use, but a decent rule of thumb is to go until the plot plateaus.
PCElbowPlot(object = tiss)

Choose the number of principal components to use.
# Set number of principal components.
n.pcs = 14
The clustering is performed based on a nearest neighbors graph. Cells that have similar expression will be joined together. The Louvain algorithm looks for groups of cells with high modularity–more connections within the group than between groups. The resolution parameter determines the scale…higher resolution will give more clusters, lower resolution will give fewer.
For the top-level clustering, aim to under-cluster instead of over-cluster. It will be easy to subset groups and further analyze them below.
# Set resolution
res.used <- 0.5
tiss <- FindClusters(object = tiss, reduction.type = "pca", dims.use = 1:n.pcs,
resolution = res.used, print.output = 0, save.SNN = TRUE)
To visualize
# If cells are too spread out, you can raise the perplexity. If you have few cells, try a lower perplexity (but never less than 10).
tiss <- RunTSNE(object = tiss, dims.use = 1:n.pcs, seed.use = 10, perplexity=30)
# note that you can set do.label=T to help label individual clusters
TSNEPlot(object = tiss, do.label = T)

Check expression of genes of interset.

Dotplots let you see the intensity of exppression and the fraction of cells expressing for each of your genes of interest.


How big are the clusters?
table(tiss@ident)
0 1 2 3 4 5 6 7 8
563 411 406 342 291 167 149 47 29
Which markers identify a specific cluster?
#clust.markers <- FindMarkers(object = tiss, ident.1 = 3, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)
print(x = head(x= clust.markers, n = 10))
You can also compute all markers for all clusters at once. This may take some time.
#tiss.markers <- FindAllMarkers(object = tiss, only.pos = TRUE, min.pct = 0.25, thresh.use = 0.25)
Display the top markers you computed above.
#tiss.markers %>% group_by(cluster) %>% top_n(5, avg_diff)
Assigning cell type identity to clusters
At a coarse level, we can use canonical markers to match the unbiased clustering to known cell types:
# stash current cluster IDs
tiss <- StashIdent(object = tiss, save.name = "cluster.ids")
# enumerate current cluster IDs and the labels for them
cluster.ids <- c(0, 1, 2, 3, 4, 5, 6, 7, 8)
free_annotation <- c("basal cell", "luminal progenitor", "basal cell", "basal cell", "stromal cell",
"mature luminal cell", "stromal cell", "endothelial cell", "basal cell")
cell_ontology_class <-c("basal cell", "luminal epithelial cell of mammary gland", "basal cell", "basal cell", "stromal cell", "luminal epithelial cell of mammary gland", "stromal cell",
"endothelial cell", "basal cell")
validate_cell_ontology(cell_ontology_class)
cell_ontology_id = convert_to_cell_ontology_id(cell_ontology_class)
tiss@meta.data['free_annotation'] <- as.character(plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = free_annotation))
validate_cell_ontology(cell_ontology_class)
cell_ontology_id = convert_to_cell_ontology_id(cell_ontology_class)
tiss@meta.data[,'free_annotation'] <- plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = free_annotation)
tiss@meta.data[,'cell_ontology_class'] <- plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = cell_ontology_class)
tiss@meta.data[,'cell_ontology_id'] <- plyr::mapvalues(x = tiss@ident, from = cluster.ids, to = cell_ontology_id)

Checking for batch effects
Color by metadata, like plate barcode, to check for batch effects.

Print a table showing the count of cells in each identity category from each plate.
table(as.character(tiss@ident), as.character(tiss@meta.data$plate.barcode))
B000166 B000167 B000168 B002309 B002432 B002433 B002435 B002436 B002437 B002438
0 7 5 14 67 101 116 26 27 63 137
1 44 46 22 33 53 53 38 45 28 49
2 78 125 80 54 14 10 14 29 1 1
3 8 9 7 10 27 30 85 111 44 11
4 18 11 8 21 43 48 22 44 26 50
5 13 7 19 18 22 18 11 25 13 21
6 3 6 7 3 24 21 22 28 16 19
7 3 4 1 9 9 8 5 2 1 5
8 0 0 1 3 11 5 1 1 0 7
Subcluster
Additional subclustering on Basal cells did not turn up definitive subtypes.
subtiss = SubsetData(tiss, ident.use = c(0, 2, 3, 8))
subtiss <- subtiss %>% ScaleData() %>%
FindVariableGenes(do.plot = TRUE, x.high.cutoff = Inf, y.cutoff = 0.5) %>%
RunPCA(do.print = FALSE)
subtiss@hvg.info
PCHeatmap(object = subtiss, pc.use = 1:3, cells.use = 500, do.balanced = TRUE, label.columns = FALSE, num.genes = 8)
PCElbowPlot(subtiss)
PCAPlot(subtiss)
sub.n.pcs = 10
sub.res.use = 1
subtiss <- subtiss %>% FindClusters(reduction.type = "pca", dims.use = 1:sub.n.pcs,
resolution = sub.res.use, print.output = 0, save.SNN = TRUE) %>%
RunTSNE(dims.use = 1:sub.n.pcs, seed.use = 10, perplexity=20)
TSNEPlot(object = subtiss, do.label = T, pt.size = 1.2, label.size = 4)
VlnPlot(subtiss, "Car3")
cluster_8 <- FindMarkers(subtiss, ident.1 = 8, test = "roc")
cluster_8
clust_8_genes = c('Lgals3', 'Ly6d', 'S100a14', "Smoc2")
VlnPlot(subtiss, clust_8_genes)
Save the Robject for later
filename = here('00_data_ingest', '04_tissue_robj_generated',
paste0("facs_", tissue_of_interest, "_seurat_tiss.Robj"))
print(filename)
[1] "/Users/josh/src/tabula-muris/00_data_ingest/04_tissue_robj_generated/facs_Mammary_seurat_tiss.Robj"
save(tiss, file=filename)
# To reload a saved object
# filename = here('00_data_ingest', '04_tissue_robj_generated',
# paste0("facs", tissue_of_interest, "_seurat_tiss.Robj"))
# load(file=filename)
LS0tCnRpdGxlOiAiTWFtbWFyeSBGQUNTIE5vdGVib29rIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKLS0tCgpTcGVjaWZ5IHRoZSB0aXNzdWUgb2YgaW50ZXJlc3QsIHJ1biB0aGUgYm9pbGVycGxhdGUgY29kZSB3aGljaCBzZXRzIHVwIHRoZSBmdW5jdGlvbnMgYW5kIGVudmlyb25tZW50LCBsb2FkIHRoZSB0aXNzdWUgb2JqZWN0LgoKYGBge3J9CnRpc3N1ZV9vZl9pbnRlcmVzdCA9ICJNYW1tYXJ5IgpsaWJyYXJ5KGhlcmUpCnNvdXJjZShoZXJlKCIwMF9kYXRhX2luZ2VzdCIsICIwMl90aXNzdWVfYW5hbHlzaXNfcm1kIiwgImJvaWxlcnBsYXRlLlIiKSkKdGlzcyA8LSBsb2FkX3Rpc3N1ZV9mYWNzKHRpc3N1ZV9vZl9pbnRlcmVzdCkKYGBgCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OH0KUENIZWF0bWFwKG9iamVjdCA9IHRpc3MsIHBjLnVzZSA9IDE6MywgY2VsbHMudXNlID0gNTAwLCBkby5iYWxhbmNlZCA9IFRSVUUsIGxhYmVsLmNvbHVtbnMgPSBGQUxTRSwgbnVtLmdlbmVzID0gOCkKYGBgCgpMYXRlciBvbiAoaW4gRmluZENsdXN0ZXJzIGFuZCBUU05FKSB5b3Ugd2lsbCBwaWNrIGEgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHVzZS4gVGhpcyBoYXMgdGhlIGVmZmVjdCBvZiBrZWVwaW5nIHRoZSBtYWpvciBkaXJlY3Rpb25zIG9mIHZhcmlhdGlvbiBpbiB0aGUgZGF0YSBhbmQsIGlkZWFsbHksIHN1cHJlc3Npbmcgbm9pc2UuIFRoZXJlIGlzIG5vIGNvcnJlY3QgYW5zd2VyIHRvIHRoZSBudW1iZXIgdG8gdXNlLCBidXQgYSBkZWNlbnQgcnVsZSBvZiB0aHVtYiBpcyB0byBnbyB1bnRpbCB0aGUgcGxvdCBwbGF0ZWF1cy4KCmBgYHtyfQpQQ0VsYm93UGxvdChvYmplY3QgPSB0aXNzKQpgYGAKCkNob29zZSB0aGUgbnVtYmVyIG9mIHByaW5jaXBhbCBjb21wb25lbnRzIHRvIHVzZS4KYGBge3J9CiMgU2V0IG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cy4gCm4ucGNzID0gMTQKYGBgCgoKVGhlIGNsdXN0ZXJpbmcgaXMgcGVyZm9ybWVkIGJhc2VkIG9uIGEgbmVhcmVzdCBuZWlnaGJvcnMgZ3JhcGguIENlbGxzIHRoYXQgaGF2ZSBzaW1pbGFyIGV4cHJlc3Npb24gd2lsbCBiZSBqb2luZWQgdG9nZXRoZXIuIFRoZSBMb3V2YWluIGFsZ29yaXRobSBsb29rcyBmb3IgZ3JvdXBzIG9mIGNlbGxzIHdpdGggaGlnaCBtb2R1bGFyaXR5LS1tb3JlIGNvbm5lY3Rpb25zIHdpdGhpbiB0aGUgZ3JvdXAgdGhhbiBiZXR3ZWVuIGdyb3Vwcy4gVGhlIHJlc29sdXRpb24gcGFyYW1ldGVyIGRldGVybWluZXMgdGhlIHNjYWxlLi4uaGlnaGVyIHJlc29sdXRpb24gd2lsbCBnaXZlIG1vcmUgY2x1c3RlcnMsIGxvd2VyIHJlc29sdXRpb24gd2lsbCBnaXZlIGZld2VyLgoKRm9yIHRoZSB0b3AtbGV2ZWwgY2x1c3RlcmluZywgYWltIHRvIHVuZGVyLWNsdXN0ZXIgaW5zdGVhZCBvZiBvdmVyLWNsdXN0ZXIuIEl0IHdpbGwgYmUgZWFzeSB0byBzdWJzZXQgZ3JvdXBzIGFuZCBmdXJ0aGVyIGFuYWx5emUgdGhlbSBiZWxvdy4KCmBgYHtyfQojIFNldCByZXNvbHV0aW9uIApyZXMudXNlZCA8LSAwLjUKCnRpc3MgPC0gRmluZENsdXN0ZXJzKG9iamVjdCA9IHRpc3MsIHJlZHVjdGlvbi50eXBlID0gInBjYSIsIGRpbXMudXNlID0gMTpuLnBjcywgCiAgICByZXNvbHV0aW9uID0gcmVzLnVzZWQsIHByaW50Lm91dHB1dCA9IDAsIHNhdmUuU05OID0gVFJVRSkKYGBgCgpUbyB2aXN1YWxpemUgCmBgYHtyfQojIElmIGNlbGxzIGFyZSB0b28gc3ByZWFkIG91dCwgeW91IGNhbiByYWlzZSB0aGUgcGVycGxleGl0eS4gSWYgeW91IGhhdmUgZmV3IGNlbGxzLCB0cnkgYSBsb3dlciBwZXJwbGV4aXR5IChidXQgbmV2ZXIgbGVzcyB0aGFuIDEwKS4KdGlzcyA8LSBSdW5UU05FKG9iamVjdCA9IHRpc3MsIGRpbXMudXNlID0gMTpuLnBjcywgc2VlZC51c2UgPSAxMCwgcGVycGxleGl0eT0zMCkKYGBgCgpgYGB7cn0KIyBub3RlIHRoYXQgeW91IGNhbiBzZXQgZG8ubGFiZWw9VCB0byBoZWxwIGxhYmVsIGluZGl2aWR1YWwgY2x1c3RlcnMKVFNORVBsb3Qob2JqZWN0ID0gdGlzcywgZG8ubGFiZWwgPSBUKQpgYGAKCkNoZWNrIGV4cHJlc3Npb24gb2YgZ2VuZXMgb2YgaW50ZXJzZXQuCgpgYGB7ciwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTh9CmdlbmVzX3RvX2NoZWNrID0gYygnS3J0MTQnLCAnS3J0MTcnLCAnS3J0NScsICdLcnQ4JywgJ0tydDE5JywgJ0tydDE4JywgJ0NkMTQnLCAnQ2VhY2FtMScsICdJdGdiMycsICdJdGdhNicsICdFbGY1JywgJ1BybHInLCAnRXNyMScsICdQZ3InLCAnRXNhbScsICdGbjEnLCAnVmltJywgJ0NkaDUnLCAnQ2l0ZWQxJywgJ0FyZWcnLCAnQ3NuMycsICdDZDU1JykKRmVhdHVyZVBsb3QodGlzcywgZ2VuZXNfdG9fY2hlY2ssIHB0LnNpemUgPSAxLCBuQ29sID0gMykKYGBgCgpEb3RwbG90cyBsZXQgeW91IHNlZSB0aGUgaW50ZW5zaXR5IG9mIGV4cHByZXNzaW9uIGFuZCB0aGUgZnJhY3Rpb24gb2YgY2VsbHMgZXhwcmVzc2luZyBmb3IgZWFjaCBvZiB5b3VyIGdlbmVzIG9mIGludGVyZXN0LgoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CiMgVG8gY2hhbmdlIHRoZSB5LWF4aXMgdG8gc2hvdyByYXcgY291bnRzLCBhZGQgdXNlLnJhdyA9IFQuCkRvdFBsb3QodGlzcywgZ2VuZXNfdG9fY2hlY2ssIGNvbC5tYXggPSAyLjUsIHBsb3QubGVnZW5kID0gVCwgZG8ucmV0dXJuID0gVCkgKyBjb29yZF9mbGlwKCkKYGBgCgpIb3cgYmlnIGFyZSB0aGUgY2x1c3RlcnM/CmBgYHtyfQp0YWJsZSh0aXNzQGlkZW50KQpgYGAKCgpXaGljaCBtYXJrZXJzIGlkZW50aWZ5IGEgc3BlY2lmaWMgY2x1c3Rlcj8KCmBgYHtyfQojY2x1c3QubWFya2VycyA8LSBGaW5kTWFya2VycyhvYmplY3QgPSB0aXNzLCBpZGVudC4xID0gMywgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCmBgYAoKYGBge3J9CnByaW50KHggPSBoZWFkKHg9IGNsdXN0Lm1hcmtlcnMsIG4gPSAxMCkpCmBgYAoKWW91IGNhbiBhbHNvIGNvbXB1dGUgYWxsIG1hcmtlcnMgZm9yIGFsbCBjbHVzdGVycyBhdCBvbmNlLiBUaGlzIG1heSB0YWtlIHNvbWUgdGltZS4KYGBge3J9CiN0aXNzLm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMob2JqZWN0ID0gdGlzcywgb25seS5wb3MgPSBUUlVFLCBtaW4ucGN0ID0gMC4yNSwgdGhyZXNoLnVzZSA9IDAuMjUpCmBgYAoKRGlzcGxheSB0aGUgdG9wIG1hcmtlcnMgeW91IGNvbXB1dGVkIGFib3ZlLgpgYGB7cn0KI3Rpc3MubWFya2VycyAlPiUgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIHRvcF9uKDUsIGF2Z19kaWZmKQpgYGAKCiMjIEFzc2lnbmluZyBjZWxsIHR5cGUgaWRlbnRpdHkgdG8gY2x1c3RlcnMKCkF0IGEgY29hcnNlIGxldmVsLCB3ZSBjYW4gdXNlIGNhbm9uaWNhbCBtYXJrZXJzIHRvIG1hdGNoIHRoZSB1bmJpYXNlZCBjbHVzdGVyaW5nIHRvIGtub3duIGNlbGwgdHlwZXM6CgoKYGBge3J9CiMgc3Rhc2ggY3VycmVudCBjbHVzdGVyIElEcwp0aXNzIDwtIFN0YXNoSWRlbnQob2JqZWN0ID0gdGlzcywgc2F2ZS5uYW1lID0gImNsdXN0ZXIuaWRzIikKCiMgZW51bWVyYXRlIGN1cnJlbnQgY2x1c3RlciBJRHMgYW5kIHRoZSBsYWJlbHMgZm9yIHRoZW0KY2x1c3Rlci5pZHMgPC0gYygwLCAxLCAyLCAzLCA0LCA1LCA2LCA3LCA4KQpmcmVlX2Fubm90YXRpb24gPC0gYygiYmFzYWwgY2VsbCIsICJsdW1pbmFsIHByb2dlbml0b3IiLCAiYmFzYWwgY2VsbCIsICJiYXNhbCBjZWxsIiwgInN0cm9tYWwgY2VsbCIsCiAgICAgICAgICAgICAgICAgICAgICJtYXR1cmUgbHVtaW5hbCBjZWxsIiwgInN0cm9tYWwgY2VsbCIsICJlbmRvdGhlbGlhbCBjZWxsIiwgImJhc2FsIGNlbGwiKQoKY2VsbF9vbnRvbG9neV9jbGFzcyA8LWMoImJhc2FsIGNlbGwiLCAibHVtaW5hbCBlcGl0aGVsaWFsIGNlbGwgb2YgbWFtbWFyeSBnbGFuZCIsICJiYXNhbCBjZWxsIiwgImJhc2FsIGNlbGwiLCAic3Ryb21hbCBjZWxsIiwgImx1bWluYWwgZXBpdGhlbGlhbCBjZWxsIG9mIG1hbW1hcnkgZ2xhbmQiLCAic3Ryb21hbCBjZWxsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJlbmRvdGhlbGlhbCBjZWxsIiwgImJhc2FsIGNlbGwiKQoKCnZhbGlkYXRlX2NlbGxfb250b2xvZ3koY2VsbF9vbnRvbG9neV9jbGFzcykKY2VsbF9vbnRvbG9neV9pZCA9IGNvbnZlcnRfdG9fY2VsbF9vbnRvbG9neV9pZChjZWxsX29udG9sb2d5X2NsYXNzKQoKdGlzc0BtZXRhLmRhdGFbJ2ZyZWVfYW5ub3RhdGlvbiddIDwtIGFzLmNoYXJhY3RlcihwbHlyOjptYXB2YWx1ZXMoeCA9IHRpc3NAaWRlbnQsIGZyb20gPSBjbHVzdGVyLmlkcywgdG8gPSBmcmVlX2Fubm90YXRpb24pKQoKdmFsaWRhdGVfY2VsbF9vbnRvbG9neShjZWxsX29udG9sb2d5X2NsYXNzKQpjZWxsX29udG9sb2d5X2lkID0gY29udmVydF90b19jZWxsX29udG9sb2d5X2lkKGNlbGxfb250b2xvZ3lfY2xhc3MpCgp0aXNzQG1ldGEuZGF0YVssJ2ZyZWVfYW5ub3RhdGlvbiddIDwtIHBseXI6Om1hcHZhbHVlcyh4ID0gdGlzc0BpZGVudCwgZnJvbSA9IGNsdXN0ZXIuaWRzLCB0byA9IGZyZWVfYW5ub3RhdGlvbikKdGlzc0BtZXRhLmRhdGFbLCdjZWxsX29udG9sb2d5X2NsYXNzJ10gPC0gcGx5cjo6bWFwdmFsdWVzKHggPSB0aXNzQGlkZW50LCBmcm9tID0gY2x1c3Rlci5pZHMsIHRvID0gY2VsbF9vbnRvbG9neV9jbGFzcykKdGlzc0BtZXRhLmRhdGFbLCdjZWxsX29udG9sb2d5X2lkJ10gPC0gcGx5cjo6bWFwdmFsdWVzKHggPSB0aXNzQGlkZW50LCBmcm9tID0gY2x1c3Rlci5pZHMsIHRvID0gY2VsbF9vbnRvbG9neV9pZCkKYGBgCgpgYGB7cn0KVFNORVBsb3Qob2JqZWN0ID0gdGlzcywgZG8ubGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC41LCBncm91cC5ieT0nZnJlZV9hbm5vdGF0aW9uJykKYGBgCgoKIyMgQ2hlY2tpbmcgZm9yIGJhdGNoIGVmZmVjdHMKCkNvbG9yIGJ5IG1ldGFkYXRhLCBsaWtlIHBsYXRlIGJhcmNvZGUsIHRvIGNoZWNrIGZvciBiYXRjaCBlZmZlY3RzLgpgYGB7cn0KVFNORVBsb3Qob2JqZWN0ID0gdGlzcywgZG8ucmV0dXJuID0gVFJVRSwgZ3JvdXAuYnkgPSAicGxhdGUuYmFyY29kZSIpCmBgYAoKUHJpbnQgYSB0YWJsZSBzaG93aW5nIHRoZSBjb3VudCBvZiBjZWxscyBpbiBlYWNoIGlkZW50aXR5IGNhdGVnb3J5IGZyb20gZWFjaCBwbGF0ZS4KCmBgYHtyfQp0YWJsZShhcy5jaGFyYWN0ZXIodGlzc0BpZGVudCksIGFzLmNoYXJhY3Rlcih0aXNzQG1ldGEuZGF0YSRwbGF0ZS5iYXJjb2RlKSkKYGBgCgoKIyMgU3ViY2x1c3RlcgoKQWRkaXRpb25hbCBzdWJjbHVzdGVyaW5nIG9uIEJhc2FsIGNlbGxzIGRpZCBub3QgdHVybiB1cCBkZWZpbml0aXZlIHN1YnR5cGVzLgoKYGBge3J9CnN1YnRpc3MgPSBTdWJzZXREYXRhKHRpc3MsIGlkZW50LnVzZSA9IGMoMCwgMiwgMywgOCkpCmBgYAoKYGBge3J9CnN1YnRpc3MgPC0gc3VidGlzcyAlPiUgU2NhbGVEYXRhKCkgJT4lIAogIEZpbmRWYXJpYWJsZUdlbmVzKGRvLnBsb3QgPSBUUlVFLCB4LmhpZ2guY3V0b2ZmID0gSW5mLCB5LmN1dG9mZiA9IDAuNSkgJT4lCiAgUnVuUENBKGRvLnByaW50ID0gRkFMU0UpCmBgYAoKYGBge3J9CnN1YnRpc3NAaHZnLmluZm8KYGBgCgoKYGBge3J9ClBDSGVhdG1hcChvYmplY3QgPSBzdWJ0aXNzLCBwYy51c2UgPSAxOjMsIGNlbGxzLnVzZSA9IDUwMCwgZG8uYmFsYW5jZWQgPSBUUlVFLCBsYWJlbC5jb2x1bW5zID0gRkFMU0UsIG51bS5nZW5lcyA9IDgpClBDRWxib3dQbG90KHN1YnRpc3MpCmBgYAoKYGBge3J9ClBDQVBsb3Qoc3VidGlzcykKYGBgCgoKYGBge3J9CnN1Yi5uLnBjcyA9IDEwCnN1Yi5yZXMudXNlID0gMQpzdWJ0aXNzIDwtIHN1YnRpc3MgJT4lIEZpbmRDbHVzdGVycyhyZWR1Y3Rpb24udHlwZSA9ICJwY2EiLCBkaW1zLnVzZSA9IDE6c3ViLm4ucGNzLCAKICAgIHJlc29sdXRpb24gPSBzdWIucmVzLnVzZSwgcHJpbnQub3V0cHV0ID0gMCwgc2F2ZS5TTk4gPSBUUlVFKSAlPiUKICAgIFJ1blRTTkUoZGltcy51c2UgPSAxOnN1Yi5uLnBjcywgc2VlZC51c2UgPSAxMCwgcGVycGxleGl0eT0yMCkKClRTTkVQbG90KG9iamVjdCA9IHN1YnRpc3MsIGRvLmxhYmVsID0gVCwgcHQuc2l6ZSA9IDEuMiwgbGFiZWwuc2l6ZSA9IDQpCgpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpGZWF0dXJlUGxvdChzdWJ0aXNzLCBjKCJDYXIzIikpCmBgYAoKYGBge3IsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQpEb3RQbG90KHN1YnRpc3MsIGdlbmVzX3RvX2NoZWNrLCBjb2wubWF4ID0gMi41LCBwbG90LmxlZ2VuZCA9IFQsIGRvLnJldHVybiA9IFQpICsgY29vcmRfZmxpcCgpCmBgYAoKYGBge3J9ClZsblBsb3Qoc3VidGlzcywgIkNhcjMiKQpgYGAKCmBgYHtyfQpjbHVzdGVyXzggPC0gRmluZE1hcmtlcnMoc3VidGlzcywgaWRlbnQuMSA9IDgsIHRlc3QgPSAicm9jIikKYGBgCgpgYGB7cn0KY2x1c3Rlcl84CmBgYAoKYGBge3J9CmNsdXN0XzhfZ2VuZXMgPSBjKCdMZ2FsczMnLCAnTHk2ZCcsICdTMTAwYTE0JywgIlNtb2MyIikKYGBgCgpgYGB7cn0KVmxuUGxvdChzdWJ0aXNzLCBjbHVzdF84X2dlbmVzKQpgYGAKCiMgU2F2ZSB0aGUgUm9iamVjdCBmb3IgbGF0ZXIKCmBgYHtyfQpmaWxlbmFtZSA9IGhlcmUoJzAwX2RhdGFfaW5nZXN0JywgJzA0X3Rpc3N1ZV9yb2JqX2dlbmVyYXRlZCcsIAogICAgICAgICAgICAgICAgICAgICBwYXN0ZTAoImZhY3NfIiwgdGlzc3VlX29mX2ludGVyZXN0LCAiX3NldXJhdF90aXNzLlJvYmoiKSkKcHJpbnQoZmlsZW5hbWUpCnNhdmUodGlzcywgZmlsZT1maWxlbmFtZSkKYGBgCgpgYGB7cn0KIyBUbyByZWxvYWQgYSBzYXZlZCBvYmplY3QKIyBmaWxlbmFtZSA9IGhlcmUoJzAwX2RhdGFfaW5nZXN0JywgJzA0X3Rpc3N1ZV9yb2JqX2dlbmVyYXRlZCcsIAojICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiZmFjcyIsIHRpc3N1ZV9vZl9pbnRlcmVzdCwgIl9zZXVyYXRfdGlzcy5Sb2JqIikpCiMgbG9hZChmaWxlPWZpbGVuYW1lKQpgYGAKCiMgRXhwb3J0IHRoZSBmaW5hbCBtZXRhZGF0YQoKYGBge3J9CmhlYWQodGlzc0BtZXRhLmRhdGEpCmBgYAoKYGBge3J9CmZpbGVuYW1lID0gaGVyZSgnMDBfZGF0YV9pbmdlc3QnLCAnMDNfdGlzc3VlX2Fubm90YXRpb25fY3N2JywgCiAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCh0aXNzdWVfb2ZfaW50ZXJlc3QsICJfYW5ub3RhdGlvbi5jc3YiKSkKd3JpdGUuY3N2KEZldGNoRGF0YSh0aXNzLCBjKCdwbGF0ZS5iYXJjb2RlJywnY2VsbF9vbnRvbG9neV9jbGFzcycsJ2NlbGxfb250b2xvZ3lfaWQnLCAnZnJlZV9hbm5vdGF0aW9uJywgJ3RTTkVfMScsICd0U05FXzInKSksIGZpbGU9ZmlsZW5hbWUpCmBgYAo=